package cn.com.twoke.application.game.mario.game;

import cn.com.twoke.application.game.mario.core.engine.ECSGame;
import cn.com.twoke.application.game.mario.core.ecs.world.GameWorld;
import cn.com.twoke.application.game.mario.game.component.*;
import cn.com.twoke.application.game.mario.game.level.file.JsonFileLevelData;
import cn.com.twoke.application.game.mario.resource.texture.TextureFactory;
import cn.com.twoke.application.game.mario.core.ecs.component.ColliderComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.PositionComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.SizeComponent;
import cn.com.twoke.application.game.mario.core.ecs.entity.Entity;
import cn.com.twoke.application.game.mario.core.ecs.level.LevelManager;
import cn.com.twoke.application.game.mario.core.ecs.level.LevelManagerImpl;
import cn.com.twoke.application.game.mario.game.level.file.JsonFileLevel;
import cn.com.twoke.application.game.mario.core.ecs.system.PhysicsSystem;
import cn.com.twoke.application.game.mario.core.ecs.system.RenderSystem;
import cn.com.twoke.application.game.mario.game.config.Settings;
import cn.com.twoke.application.game.mario.game.system.AutoMovingSystem;
import cn.com.twoke.application.game.mario.game.system.PlayerControlSystem;
import cn.com.twoke.application.game.mario.game.system.TransfersSystem;
import cn.com.twoke.application.game.mario.game.util.TextureUtils;
import cn.com.twoke.application.game.mario.game.config.Tiles;
import cn.hutool.core.lang.Opt;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 游戏界面是16x15
 */
public class SuperMarioGame extends ECSGame {
    static BufferedImage tileset1;
    static BufferedImage tileset2;
    static BufferedImage marioSprite;// 精灵图

    static BufferedImage background1;

    static {
        // 加载游戏配置
        cn.com.twoke.application.game.mario.game.config.ConfigLoader.loadConfig();
        
        background1 = TextureUtils.loadImage("texture/background1.png");
        tileset1 = TextureUtils.loadImage("texture/tileset1.png");
        tileset2 = TextureUtils.loadImage("texture/tileset2.png");
        marioSprite = TextureUtils.loadImage("texture/mario.png");
    }


    public SuperMarioGame() {
        super("超级玛丽 v1.0", Settings.width(), Settings.height());
        this.setResizable(false);
    }


    public static void enableDebug() {
        TransfersComponent.enableDebug(); // 开启传送门UI调试
        ColliderComponent.enableDebug(); // 开启碰撞体调试
        SceneComponent.enableDebug(); // 开启场景相机调试
    }

    private  LevelManager levelManager;
    @Override
    protected void init(GameWorld world) {
        this.levelManager = new LevelManagerImpl(world, this.sceneManager);
        TextureFactory.register("normal-block", this::groundTexture);
        TextureFactory.register("background", this::backgroundTexture);
        TextureFactory.register("pipe", this::pipeTexture);
        // 添加第一关
        this.levelManager.load(JsonFileLevelData.builder().fileName("1-1").build());
        this.levelManager.load(JsonFileLevelData.builder().fileName("1-1").build());
    }

    private void backgroundTexture(TextureFactory.RenderContext renderContext) {
        Graphics2D g = renderContext.getGraphics();
        SizeComponent component = renderContext.getEntity().getComponent(SizeComponent.class);
        PositionComponent positionComponent = renderContext.getEntity().getComponent(PositionComponent.class);
        int backgroundWidth = Tiles.widthOf(background1.getWidth() / 16);
        int backgroundHeight = Tiles.heightOf(background1.getHeight() / 16);
        int drawCount = component.width / backgroundWidth;
        int i = 0;
        for (i = 0; i < drawCount; i++) {
            g.drawImage(background1, backgroundWidth * i,(int)positionComponent.y, backgroundWidth, backgroundHeight,null);
        }
        int width = component.width % backgroundWidth;
        if (width > 0) {
            g.drawImage(background1.getSubimage((int)positionComponent.x,(int)positionComponent.y, Tiles.unscaleOf(width), background1.getHeight()),backgroundWidth * i, 0,
                    width, backgroundHeight
                    ,null
            );
        }
    }

    /**
     * 管道纹理贴图
     *
     * @param ctx
     */
    private void pipeTexture(TextureFactory.RenderContext ctx) {
        Entity entity = ctx.getEntity();
        if (entity.hasComponent(PipeComponent.class)) {
            Graphics2D g = ctx.getGraphics();
            PipeComponent pipe = entity.getComponent(PipeComponent.class);
            Function<BufferedImage, BufferedImage> dealFun = Opt.ofNullable(entity.getComponent(TextureDealComponent.class)).map(TextureDealComponent::getTextureDealFun).orElse(Function.identity());

            for (PipeComponent.PipePart pipePart : pipe.pipeParts) {
                Rectangle shape = pipePart.getShape();
                if (pipePart.getType() == PipeComponent.PipePartType.ENTRY_TOP) {
                    g.drawImage(dealFun.apply(tileset2.getSubimage(7 * 16, 0, 16 * 2, 16)), shape.x, shape.y, shape.width, shape.height, null);
                } else if (pipePart.getType() == PipeComponent.PipePartType.VERTICAL) {
                    int count = shape.height / Tiles.heightOf(1);
                    for (int i = 0; i < count; i++) {
                        if (pipe.isEmbeddedWall) {
                            g.drawImage(dealFun.apply(tileset2.getSubimage(7 * 16, 16, 16, 16)), shape.x, shape.y + Tiles.heightOf(i), shape.width, Tiles.heightOf(1), null);
                        } else {
                            g.drawImage(dealFun.apply(tileset2.getSubimage(7 * 16, 16, 16 * 2, 16)), shape.x, shape.y + Tiles.heightOf(i), shape.width, Tiles.heightOf(1), null);
                        }
                    }
                } else if (pipePart.getType() == PipeComponent.PipePartType.HORIZONTAL) {
                    int count = shape.width / Tiles.widthOf(1);
                    for (int i = 0; i < count; i++) {
                        g.drawImage(dealFun.apply(tileset2.getSubimage(6 * 16, 16 * 2, 16, 16 * 2)), shape.x + Tiles.heightOf(i), shape.y, Tiles.widthOf(1), shape.height, null);
                    }
                } else if (pipePart.getType() == PipeComponent.PipePartType.LEFT_JOINT) {
                    if (pipe.isEmbeddedWall) {
                        g.drawImage(dealFun.apply(tileset2.getSubimage(7 * 16, 16 * 2, 16, 16 * 2)), shape.x, shape.y, Tiles.widthOf(1), shape.height, null);
                    } else {
                        g.drawImage(dealFun.apply(tileset2.getSubimage(7 * 16, 16 * 2, 16, 16 * 2)), shape.x, shape.y, Tiles.widthOf(1), shape.height, null);
                        g.drawImage(dealFun.apply(tileset2.getSubimage(8 * 16, 16 * 2, 16, 16 * 2)), shape.x + Tiles.widthOf(1), shape.y, Tiles.widthOf(1), shape.height, null);
                    }
                } else if (pipePart.getType() == PipeComponent.PipePartType.ENTRY_LEFT) {
                    g.drawImage(dealFun.apply(tileset2.getSubimage(5 * 16, 16 * 2, 16, 16 * 2)), shape.x, shape.y, shape.width, shape.height, null);
                }
            }
        }
    }

    /**
     * 地面
     *
     * @param ctx
     */
    private void groundTexture(TextureFactory.RenderContext ctx) {
        Entity entity = ctx.getEntity();
        if (entity.hasComponent(SizeComponent.class) && entity.hasComponent(PositionComponent.class)) {
            Graphics2D g = ctx.getGraphics();
            SizeComponent size = entity.getComponent(SizeComponent.class);
            PositionComponent position = entity.getComponent(PositionComponent.class);
            Tiles tiles = Tiles.form(size.width, size.height);
            BlockRenderFillComponent fillComponent = entity.getComponent(BlockRenderFillComponent.class);
            for (int i = 0; i < tiles.getRows(); i++) {
                for (int j = 0; j < tiles.getCols(); j++) {
                    Rectangle tile = new Rectangle((int) position.x + j * Tiles.widthOf(1), (int) position.y + i * Tiles.heightOf(1), Tiles.widthOf(1), Tiles.heightOf(1));
                    BufferedImage ground = Opt.ofNullable(fillComponent).map(BlockRenderFillComponent::getBlock).map(Supplier::get).orElse(tileset1.getSubimage(0, 0, 16, 16));
                    g.drawImage(ground, tile.x, tile.y, tile.width, tile.height, null);
                }
            }
        }
    }

    private static void drawDebugTransfer(Entity entity, Graphics2D g2d) {
        if (entity.hasComponent(TransfersComponent.class) && TransfersComponent.isDebug()) {
            TransfersComponent component = entity.getComponent(TransfersComponent.class);
            for (TransfersComponent.Transfer entry : component.getEntries()) {
                g2d.setColor(Color.yellow);
                g2d.fill(entry.getShape());
                if (entry.enabled) {
                    g2d.setFont(new Font(Font.MONOSPACED, Font.BOLD, 16));
                    g2d.setColor(Color.white);
                    Rectangle bounds = entry.shape.getBounds();
                    FontMetrics fm = g2d.getFontMetrics();
                    if (entry.direction == TransfersComponent.Direction.TOP) {
                        g2d.drawString(entry.getName(), bounds.x + bounds.width / 2 - fm.stringWidth(entry.getName()) / 2, bounds.y - Tiles.scaleOf(4));
                    } else {
                        g2d.drawString(entry.getName(), bounds.x - Tiles.scaleOf(fm.stringWidth(entry.getName())), bounds.y + bounds.height / 2 - fm.getHeight() / 2 + fm.getAscent());
                    }
                }
            }
        }
    }

    @Override
    protected void initGameSystem(GameWorld world) {
        world.addSystem(new TransfersSystem());
        world.addSystem(RenderSystem.builder()
                        .inputState(this.inputState)
                        .sceneManager(this.sceneManager)
                .build().addAfterHook(SuperMarioGame::drawDebugTransfer));
        PhysicsSystem physicsSystem = new PhysicsSystem();
        world.addSystem(physicsSystem);
        world.addSystem(new PlayerControlSystem(this.inputState));
        world.addSystem(new AutoMovingSystem());
    }

    /**
     * 初始化游戏实体
     *
     * @param world
     */
    @Override
    protected void initGameEntities(GameWorld world) {
        levelManager.checkin("1-1"); // 初始化时进入1-1关卡
    }

}




